This article will go through the steps required to set up a test project to test custom XPages controls.
, but same steps can be applied to other controls.
plugin.
.
from the test framework project, rename to match your class.
, Run As, JUnit Test. It fails with:
again (using the Run button in the toolbar). The first test fails with:
In the Package Explorer view, click on the View Menu (a little down triangle to the right of the "Package Explorer" view name)
Re-run the test suite. If the 2nd test is still giving the same error you may have an issue with your classpath dependancy ordering
com.ibm.xsp.test.framework.lifecycle.RegisteredDecodeTest
testDecodeRegisteredComponents(com.ibm.xsp.test.framework.lifecycle.RegisteredDecodeTest)
com.ibm.xsp.FacesExceptionEx: java.lang.NoClassDefFoundError: lotus/domino/NotesException
at com.ibm.xsp.config.CLBootStrap.initContext(CLBootStrap.java:89)
at com.ibm.xsp.config.BootStrap.init(BootStrap.java:82)
at com.ibm.xsp.test.framework.TestProject.bootstrap(TestProject.java:405)
at com.ibm.xsp.test.framework.TestProject.createRequest(TestProject.java:368)
at com.ibm.xsp.test.framework.TestProject.createFacesContext(TestProject.java:340)
at com.ibm.xsp.test.framework.lifecycle.RegisteredDecodeTest.testDecodeRegisteredComponents(RegisteredDecodeTest.java:77) Caused by: java.lang.NoClassDefFoundError: lotus/domino/NotesException
- Edit your JRE configuration to add the
Notes.jar and
njempcl.jar as follows:
In eclipse, menu, Window, Preferences, Java, Installed JREs, select the JRE and click Edit.
Add External JARs, browse to C:\Notes\jvm\lib\ext\, and select both .jars:
njempcl.jar,
Notes.jar
OK, Finish, OK. Wait for it to finish compiling, rerun the JUnit tests.
- If one of the tests fail with:
com.ibm.xsp.FacesExceptionEx: java.lang.NoClassDefFoundError: com.ibm.designer.domino.napi.NotesAPIException
.........
Caused by: java.lang.NoClassDefFoundError: com.ibm.designer.domino.napi.NotesAPIException
- If one of the tests now fails with:
java.lang.ClassNotFoundException: com.ibm.domino.napi.NException
- In the Manifest, add a dependancy on
com.ibm.domino.napi
Rerun the tests
- If the 2nd test now fails with:
java.lang.NoClassDefFoundError: com/ibm/designer/runtime/domino/adapter/util/PageNotFoundException
- In the Mainfest add a dependancy on
com.ibm.domino.xsp.adapter
Rerun the tests.
- The 2nd test passes, but the console still contains:
java.lang.NoClassDefFoundError: com/ibm/designer/runtime/domino/bootstrap/BootstrapEnvironment
at com.ibm.domino.xsp.module.nsf.platform.Factory.createPlatform(Factory.java:34)
- In the Mainfest add a dependancy on
com.ibm.domino.xsp.bootstrap
- The Console still contains:
java.lang.UnsatisfiedLinkError: no nlsxbe in java.library.path
at java.lang.ClassLoader.loadLibrary(Unknown Source)
- You can ignore that.
- Now lets look at that first JUnit fail:
com.ibm.xsp.test.framework.translator.BaseGeneratePagesTest
testPagesNotChanged(com.ibm.xsp.test.framework.translator.BaseGeneratePagesTest)
junit.framework.AssertionFailedError: No pages found to translate.
at com.ibm.xsp.test.framework.translator.GeneratePagesTest.testPagesNotChanged(GeneratePagesTest.java:102)
- That fail indicates that there are no test .xsp files within the test project
- At the project root, create a folder named
pages (just a convention - it will actually search through all folders),
and create a .xsp file, containing a simple XPage that uses your control, like:
http://www.ibm.com/xsp/core" xmlns:eg="http://example.com/xsp/control">
- Re-run the test suite. Now the test fails with an unknown namespace problem:
com.ibm.xsp.test.framework.translator.BaseGeneratePagesTest
testPagesNotChanged(com.ibm.xsp.test.framework.translator.BaseGeneratePagesTest)
junit.framework.AssertionFailedError: 1 fail(s). :
[0] # /pages/simpleTestOfExampleControl.xsp Problem translating file: Unknown component namespace for the tag eg:exampleControl, with the namespace
http://example.com/xsp/control.
at com.ibm.xsp.test.framework.translator.GeneratePagesTest.testPagesNotChanged(GeneratePagesTest.java:200)
- That problem is occurring because the translator doesn't know that this
test project depends on the library (the
config.properties dependancy above
is only used by the tests, it is not passed to the design-time code in the translator.
- Configure an
xsp.properties file listing the libraries
this test project depends on (equivalent to changing an application's Application Properties, Advanced tab, XPages libraries list):
- At the project root, create a folder
WEB-INF
and create
xsp.properties file with the contents
(the ..xsp.core library is always required):
xsp.library.depends=\
com.ibm.xsp.core.library,\
com.ibm.xsp.extsn.library,\
com.ibm.xsp.designer.library,\
com.ibm.xsp.domino.library,\
com.example.library
- Now re-run the test suite. It now fails with:
com.ibm.xsp.test.framework.translator.BaseGeneratePagesTest
testPagesNotChanged(com.ibm.xsp.test.framework.translator.BaseGeneratePagesTest)
junit.framework.AssertionFailedError: 1 fail(s). :
[0] * /pages/simpleTestOfExampleControl.xsp File generated, refresh the project.
at com.ibm.xsp.test.framework.translator.GeneratePagesTest.testPagesNotChanged(GeneratePagesTest.java:200)
- That
BaseGeneratePagesTest is not really a unit test, it is a utility class to
generate the
.java file corresponding to each .xsp file. When running the test suite,
you must first generate the
.java files, then refresh the project so that eclipse
detects and compiles the
.java files to
.class files, then run the main test suite.
The
BaseGeneratePagesTest does actually function as a unit test, to remind you that
you have forgotten to generate the
.java files, so the .class files will not be available
so any tests that attempt to load and render the
.xsp pages will fail.
- It is best to make a subclass of
BaseGeneratePagesTest, to make it easier
to run
.java file generation outside of running the entire test suite.
- Create a
..translator(in our case it's
xsp.example.test.translator) package in your test project, and create a class
ExampleGeneratePagesTest extending
BaseGeneratePagesTest.
Right-click on that class, Run As, JUnit test.
- Refresh the project, you will see a new
gen/ folder created at the project root.
You should configure that to be treated as a Java Source Folder,
so the contents are compiled.
- Right-click on the project, Properties, Java Build Path, first tab(
Source), Add Folder,
select the "gen" folder, OK, OK.
You will see the
gen/ source folder contains a
translation.properties file,
and a
xsp.pages package with one
.java file per
.xsp file.
If you create subfolders under your
pages/ folder, there will be subpackages
under that package.
- Run the *
GeneratePages*(in our case it's
ExampleGeneratePagesTest) test, refresh the project, run the test suite. The first test will pass.
- If
BaseViewSerializeTest test is failing:
com.ibm.xsp.test.framework.serialize.BaseViewSerializeTest
testAllViews(com.ibm.xsp.test.framework.serialize.BaseViewSerializeTest)
junit.framework.AssertionFailedError: 1 fail(s). :
Failed on view [0] /pages/simpleTestOfExampleControl.xsp with: /pages/simpleTestOfExampleControl UIViewRootEx2.getFacetsAndChildren()[0] get method should not return a UIComponent
at com.ibm.xsp.test.framework.serialize.ViewSerializeTest.testAllViews(ViewSerializeTest.java:151)
- That test creates the control tree for each .xsp file,
serializes and restores the tree and verifies the restored control tree
matches the original tree.
- Create a new
..serialize(in our case it's
xsp.example.test.serialize) package, and create an
ExampleViewSerializeTest,
extending from
BaseViewSerializeTest.
- In
ExampleTestSuite, change the reference to
BaseViewSerializeTest
to refer to the new test.
- Edit the new test to add the method:
@Override
protected Object[][] getCompareSkips() {
Object[][] skips = super.getCompareSkips();
skips = XspTestUtil.concat(skips, getCompareSkips_UIComponent());
return skips;
}
- Rerun the test suite. It should pass.
- If
BaseNamingConventionTest test is failing:
com.ibm.xsp.test.framework.registry.BaseNamingConventionTest
testNamingConventions(com.ibm.xsp.test.framework.registry.BaseNamingConventionTest)
java.lang.RuntimeException: Prefix not found in config.properties, like: NamingConvention.package.prefix=com.example.foo
at com.ibm.xsp.test.framework.registry.NamingConventionTest.testNamingConventions(NamingConventionTest.java:91)
- In the test project
config.properties file add the lines:
- Package name and component-type prefix, like "com.ibm.xsp" or
- "com.ibm.xsp.extlib", used in the NamingConventionTest
- NamingConvention.package.prefix=
NamingConvention.package.prefix=com.example
- Rerun - the test still fails
because the control does not match the naming convention - it may be
that a test subclass is needed to change the convention tested.
- The
build.properties file in the test project contains:
Description Resource Path Location Type
gen/ is missing from source.. build.properties /xsp.example.test line 1 Plug-in Problem
- Open the file, click on the warning, choose the action "Add gen/ to the source.. build entry"
The first line becomes:
source.. = src/,\
gen/
- Run the test suite. There are still fails.
- Create a
..registry(in our case it's
xsp.example.test.registry) package
Create a Java Class in that package named
ExampleNamingConventionTest that extends
BaseNamingConventionTest
Copy the following contents to the newly created Java Class:
@Override
protected boolean isRequireControlSubpackageName() {
return false;
}
@Override
protected String[] getExpectedPrefixes() {
String[] expectedPrefixes =
super.getExpectedPrefixes();
[0] package-name prefix
"com.ibm.xsp",
expectedPrefixes[0] = "com.ibm.xsp.extlib";
[1] abstract component package-name suffix: usually ".component"
".component",
expectedPrefixes[1] = ".component.+";
[2] tag component package-name suffix: usually ".component" or "component.xp."
".component.xp",
expectedPrefixes[2] = ".component";
[4] tag component short java-class prefix: usually "Xsp"
"Xsp",
expectedPrefixes[4] = "";
[5] abstract component short java-class suffix: possibly "Ex" or "Ex2"
expectedPrefixes[5] = "Base";
[7] abstract component-type short-name prefix: usually "UI"
"UI",
expectedPrefixes[7] = "";
return expectedPrefixes;
}
Update
ExampleTestSuite to use
ExampleNamingConventionTest instead of
BaseNamingConventionTest
Re-run the tests. It should pass.
- If
BaseSuiteSetupTest fails:
java.lang.UnsupportedOperationException: getTestedSuiteName() method must be overridden in a subclass.
As the error message suggests, it must be overridden in a subclass.
Create a package
..setup(in our case it's
xsp.example.test.setup)
Create new Java Class
ExampleSuiteSetupTest that extends
BaseSuiteSetupTest with the following methods implemented:
@Override
protected long getTestedSuiteVersion() {
return ExampleTestSuite.
SUITE_VERSION;
}
@Override
protected String getTestedSuiteName() {
return "ExampleTestSuite";
}
Rename relevant parts according to your project needs.
Update ExampleTestSuite to use ExampleSuiteSetupTest instead of BaseSuiteSetupTest
Re-run the tests. It should pass.
- If
BaseGroupReuseTest fails:
junit.framework.AssertionFailedError: 1 fail(s). :
META-INF/exampleControl.xsp-config eg:exampleControl title Should reuse
for an existing control group: com.ibm.xsp.group.core.prop.title
Even though this is failing, it's not required for the control to function properly. For the demonstration purpose, we're going to add a skip to JUnit tests.
Copy the fail message from the JUnit results window into a text file. In our case the message is:META-INF/exampleControl.xsp-config eg:exampleControl title Should reuse for an existing control group: com.ibm.xsp.group.core.prop.title
If doesn't exist yet, create a package ..registry(in our case it's xsp.example.test.registry)
Create a Java Class ExampleGroupReuseTest in the newly created package that extends BaseGroupReuseTest with the following contents:private String[] skips =
new String[]{
"META-INF/exampleControl.xsp-config eg:exampleControl title Should reuse for an existing control group: com.ibm.xsp.group.core.prop.title"
};
@Override
protected String[] getSkipFails() {
return skips;
}
The skips variable of String[] type contains error messages that we want to ignore and make the test pass.
Update ExampleTestSuite to use ExampleGroupReuseTest instead of BaseGroupReuseTest
Re-run the tests. It should pass.
- if
BaseLabelsLocalizableTest fails:
junit.framework.AssertionFailedError: 1 fail(s). :
META-INF/exampleControl.xsp-config eg:exampleControl.mainHeader unexpected localizable prop
This test fails because the property name is not recognized as part of the localizable property list.
We are going to extend that list by overriding the Base class.
If doesn't exist yet, create a package
..registry(in our case it's
xsp.example.test.registry)
Create a Java Class
ExampleLabelsLocalizableTest in the newly created package that extends
BaseLabelsLocalizableTest with the following contents:
Custom mainTitle property to localize
private String[] extendListLocalizableNames =
new String[]{
"mainHeader"
};
protected List
getCommonLocalizableNames() {
List commonListLocalizableNames=super.getCommonLocalizableNames();
List combinedList = new ArrayList();
combinedList.addAll(Arrays.asList((String[])commonListLocalizableNames.toArray()));
combinedList.addAll(Arrays.asList(extendListLocalizableNames));
return combinedList;
} The code above specifies a new array with list of properties that should be localizable(in our case it's just one - mainHeader).
getCommonLocalizableName method calls a super-class method to get the predefined list and appends our own. It then returns the new list.
Update ExampleTestSuite to use ExampleLabelsLocalizableTest instead of BaseLabelsLocalizableTest
Re-run the tests. It should pass.
- if BaseNamingConventionErrorTest fails:junit.framework.AssertionFailedError: 2 fail(s). :
META-INF/exampleControl.xsp-config Missing required in config file, inferring subpackage []
META-INF/exampleControl.xsp-config/eg:exampleControl [Rule3] Bad component-class short name ExampleControl does not begin with Xsp
If doesn't exist yet, create a package
..registry(in our case it's
xsp.example.test.registry)
Create a Java Class
ExampleNamingConventionErrorTest in the newly created package that extends
ExampleNamingConventionTest with the following contents:
@Override
public String getDescription() {
return super.getDescription() +" [ERROR severity only]";
}
@Override
protected int getRuleSeverityLevelCutoff() {
only show Errors, not Warning and Info
return SEV_ERROR;
}Update ExampleTestSuite to use ExampleNamingConventionErrorTest instead of BaseNamingConventionErrorTest
Re-run the tests. It should pass.
- if BaseControlCategoryKnownTest fails:junit.framework.AssertionFailedError: 1 fail(s). :
META-INF/exampleControl.xsp-config eg:exampleControl unknown category: Example
As with
BaseGroupReuseTest, we are going to add skips.
Copy the fail message from the JUnit results window into a text file. In our case the message is:
META-INF/exampleControl.xsp-config eg:exampleControl unknown category: Example
If doesn't exist yet, create a package
..registry.annotate(in our case it's
xsp.example.test.registry.annotate)
Create a Java Class
ExampleControlCategoryKnownTest in the newly created package that extends
BaseControlCategoryKnownTest with the following contents:
private String[] skips = new String[]{
"META-INF/exampleControl.xsp-config eg:exampleControl unknown category: Example"
};
@Override
protected String[] getSkips() {
return skips;
}As previously, the skips variable that gets returned contains messages that need to be skipped.
Update ExampleTestSuite to use ExampleControlCategoryKnownTest instead of BaseControlCategoryKnownTest
Re-run the tests. It should pass.
- if BaseRoleAccessibilityTest fails:junit.framework.AssertionFailedError: 1 fail(s). :
META-INF/exampleControl.xsp-config eg:exampleControl Expected role property for accessibility does not exist.
Again, in this case we are adding skips to make this test pass.
Copy the fail message from the JUnit results window into a text file. In our case the message is:
META-INF/exampleControl.xsp-config eg:exampleControl Expected role property for accessibility does not exist.
If doesn't exist yet, create a package
..registry.annotate(in our case it's
xsp.example.test.registry.annotate)
Create a Java Class
ExampleRoleAccessibilityTest in the newly created package that extends
BaseRoleAccessibilityTest with the following contents:
private String[] skips = new String[]{
"META-INF/exampleControl.xsp-config eg:exampleControl Expected role property for accessibility does not exist."
};
@Override
protected String[] getSkipFails() {
TODO Auto-generated method stub
return skips;
}As previously, the skips variable that gets returned contains messages that need to be skipped.
Update ExampleTestSuite to use ExampleRoleAccessibilityTest instead of BaseRoleAccessibilityTest
Re-run the tests. It should pass.
- Most likely ExampleViewSerializeTest will fail:junit.framework.AssertionFailedError: 3 fail(s). :
Unused compare method skip: UIComponentBase.getParent()
Unused compare method skip: UIComponentBase.getFacetsAndChildren()
Unused compare method skip: UIComponentBase.getAttributes()
To fix this, open
ExampleViewSerializeTest class and comment out the following string:
skips = XspTestUtil.concat(skips, getCompareSkips_UIComponent());
Re-run the tests. It should pass.
- if BaseSinceVersionsSetTest fails:junit.framework.AssertionFailedError: 1 fail(s). version mismatch:
META-INF/exampleControl.xsp-config eg:exampleControl bad since version. Expected null(probably)<, was 1.0.0<
junit.framework.AssertionFailedError: 1 fail(s). Known tags list not available. Please override getSinceVersionLists() and list these tags.:
tag eg:exampleControl has new props: 2 {mainHeader, title}
If doesn't exist yet, create a package
..version(in our case it's
xsp.example.test.version)
Create a Java Class
ExampleSinceVersionLists with the following contents:
public static List getSinceVersionLists(){
List list = new ArrayList();
list.add(new Example100List());
return list; }
public static class Example100List implements SinceVersionList {
private Object[][] tagsAndProps = new Object[][]{
new Object[]{"prefixedTagName", newTagThisVersion, new String[]{
"propName",
"propName",
}},
new Object[]{"eg:exampleControl", true, new String[]{
"mainHeader",
"title",
}},
};
private String[] skips = new String[]{
};
public Object[][] tagsAndProps() {
return tagsAndProps;
}
public String sinceVersion() {
return "1.0.0";
}
public String[] skips() {
return skips;
}}
Create a Java Class ExampleSinceVersionsSetTest in the newly created package that extends BaseSinceVersionsSetTest with the following contents:
@Override
protected List getSinceVersionLists() {
List list = super.getSinceVersionLists();
list.addAll(ExampleSinceVersionLists.getSinceVersionLists());
return list;
}
Update
ExampleTestSuite to use
ExampleSinceVersionsSetTest instead of
BaseSinceVersionsSetTest
Re-run the tests. It should pass.
This is needed because of how XPages handles different versions of plugins. Please read this Wiki article first:
1. A library can contain multiple tags corresponding to a control(in our case it's
eg:exampleControl)
2. Each tag can have multiple properties(in our case it's
mainHeader and
title)
3. It is considered to be best practice to change your library version with each new tag/property added
4. For each new version a new class similar to the
Example100List should be created(subsequent would probably be
Example101List)
4.1 Each such class should include the newly added tags/properties only - It must not include
all properties available, only new ones.
4.2 For each such class the
sinceVersion method must be updated to return the new version
4.3
tagsAndProps variable must be updated with the newly added
tags/
properties
5. Once such class is created, add a reference to it in the
getSinceVersionLists
- If BaseReflectionSerializeTest / BaseRegisteredSerializationTest fail with:junit.framework.AssertionFailedError: 1 fail(s). :
META-INF/exampleControl.xsp-config eg:exampleControl Control class not known to registry: UIViewRootEx2
Open
config.properties located in
com.ibm.xsp.test.framework. Append the following lines to the file:
# Extra libraries whose xsp-config files should be loaded
- when creating a registry
extra.library.depends.designtime.nonapplication=\
com.ibm.xsp.extsn.libraryThe error message suggests that the component depends on the com.ibm.xsp.extsn.library plugin. This is design time plugin dependencies that are different from runtime.
In future, if your plugin depends on other library plugins this is the place where it needs to be added.
target.library property points to your own library plugin and hence is not required to be added to the extra library dependency list.
It is recommended to create a separate test project for each library plugin, but If a set a of plugins need to be tested from one test project it should be added to the extra library dependency list. Unless your target library already has a dependency on a second library plugin.
Green Test SuiteGreen test suite is used as a temporary solution to make the failing tests pass. For example, when there's a set of known issues and there's a need to track any new regression: instead of having a list with the issues you add them to the green test suite.
In the
..test package(in our case it's
xsp.example.test) create a Java Class
GreenExampleTestSuite that extends
TestSuite with the following contents:
public static Test suite() {
ExampleTestSuite mainSuite =
new ExampleTestSuite();
List
testClasses = ExampleTestSuite.getTestClassList();
if necessary replace a class with a green subclass (that will always pass), like so:
testClasses.set(testClasses.indexOf(RegisteredDecodeTest.class), GreenRegisteredDecodeTest.class);
TestClassList.addAll(mainSuite, testClasses);
GreenExampleTestSuite greenSuite = new GreenExampleTestSuite();
greenSuite.addTest(new SkipFileTestSetup(mainSuite,"junit-results.txt"));
greenSuite.addTestSuite(SkipFileUsedTest.class);
return greenSuite;
}
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
In the constructor we're are creating our main test suite class(in our case it's ExampleTestSuite) and loading all the test cases.
Then we're creating our Green test suite class(GreenExampleTestSuite) and providing a text file that will contain fail messages that we will need to skip.
In the root of your test project create a text file junit-results.txt. It is best to add these three lines at the top of the file:JUnit results, running against Notes 9.0.0
Test ran 2012-11-13 14:34
Lines in file: 59
Where in the first line you describe against which version the test suite was ran.
Lines in file: is just for tracking purpose to compare with previous results in case new issues were introduced.
Copy the failure list by right-clicking on the junit tests window and select Copy Failure List
Add an empty new line after those three lines. Again on a new line paste the failure list. The resulting text file should look something like this:JUnit results, running against Notes 9.0.0
Test ran 2012-11-13 14:34
Lines in file: 59
ExampleTestSuite
com.ibm.xsp.test.framework.SampleTestSuite
xsp.example.test.registry.ExampleLabelsLocalizableTest
testLabelsLocalizable(xsp.example.test.registry.ExampleLabelsLocalizableTest)
junit.framework.AssertionFailedError: 1 fail(s). :
META-INF/exampleControl.xsp-config eg:exampleControl.mainTitle not localizable
at junit.framework.Assert.fail(Assert.java:47)
at com.ibm.xsp.test.framework.registry.LabelsLocalizableTest.testLabelsLocalizable(LabelsLocalizableTest.java:158)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:60)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:37)
at java.lang.reflect.Method.invoke(Method.java:611)
at junit.framework.TestCase.runTest(TestCase.java:164)
at junit.framework.TestCase.runBare(TestCase.java:130)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:120)
at junit.framework.TestSuite.runTest(TestSuite.java:230)
at junit.framework.TestSuite.run(TestSuite.java:225)
at junit.framework.TestSuite.runTest(TestSuite.java:230)
at junit.framework.TestSuite.run(TestSuite.java:225)
at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
In general, the first three lines are not required but may be used for the reasons explained previously.
Now, if we run the green test suite all the failures that are present in the junit-results.txt should pass.